﻿#region Using Directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;

using CommunityServer;
using CommunityServer.Components;
using CommunityServer.Controls;

using CSLive.ControlPanel;
using CSLive.LiveID;

using Control=CSLive.ControlPanel.Editor.Control;
using Roles=CommunityServer.Components.Roles;

#endregion

namespace CSLive.Controls
{
    [ToolboxData( "<{0}:LoginLink runat=\"server\" />" )]
    public class LoginLink : ObjectDataBase, ICSLiveControl
    {
        #region Members

        private static readonly DateTime ExpireCookie = DateTime.Now.AddYears( -10 );
        private User _user;
        private WindowsLiveLogin wll;

        #endregion

        #region Events

        protected override void OnInit( EventArgs e )
        {
            string privacyurl = (string)LiveConfiguration.Instance.getConfigSetting("Live ID SSO", "Privacy Page").ToString();
            if (privacyurl.StartsWith("~") || privacyurl.StartsWith("/"))
            {
                privacyurl = string.Format("{0}://{1}{2}", Context.Request.Url.Scheme, Context.Request.Url.Host,
                                            ResolveClientUrl(privacyurl));
            }
            wll = new WindowsLiveLogin(LiveConfiguration.Instance.getConfigSetting("Live ID SSO", "Application ID").ToString(),
                                        LiveConfiguration.Instance.getConfigSetting( "Live ID SSO", "Secret" ).ToString(), null, false, privacyurl );
            base.OnInit( e );
        }

        protected override void OnLoad( EventArgs e )
        {
            State = State.Out;
            string action = Context.Request.QueryString.Get( "action" );
            if ( action == "logout" )
            {
                if ( Context.Request.Cookies[ "LiveID" ] != null )
                {
                    HttpCookie lc = Context.Request.Cookies[ "LiveID" ];
                    if ( lc != null )
                    {
                        lc.Expires = ExpireCookie;
                        Context.Response.SetCookie( lc );
                    }
                }
                Context.Response.Redirect( SiteUrls.Instance().Logout, true );
            }
            else if ( action == "clearcookie" )
            {
                if ( Context.Request.Cookies[ "LiveID" ] != null )
                {
                    HttpCookie lc = Context.Request.Cookies[ "LiveID" ];
                    if ( lc != null )
                    {
                        lc.Expires = ExpireCookie;
                        Context.Response.SetCookie( lc );
                    }
                }
                FormsAuthentication.SignOut();
                HttpCookie formsAuthCookie = FormsAuthentication.GetAuthCookie( CSContext.Current.User.Username, true );
                formsAuthCookie = AlterAuthCookie( formsAuthCookie );

                UserCookie userCookie = CSContext.Current.User.GetUserCookie();
                userCookie.DeleteCookie( formsAuthCookie );

                // Nuke the roles cookie
                Roles.SignOut();

                // after cookies are cleared, you must reply to Windows Live ID server 
                // Confirm successful sign out by sending an http response.
                byte[] content;
                string type;
                wll.GetClearCookieResponse( out type, out content );
                Context.Response.ContentType = type;
                Context.Response.OutputStream.Write( content, 0, content.Length );
                Context.Response.End();
            }
            else if ( Context.Request.Form[ "action" ] == "login" )
            {
                WindowsLiveLogin.User user = wll.ProcessLogin( Context.Request.Form );
                if (user.Context == "register")
                {
                    Context.Response.Cookies.Add(new HttpCookie("LiveID", user.Id));
                    Context.Response.Redirect(wll.GetConsentUrl("Contacts.Invite", null, string.Format("{0}://{1}{2}", Context.Request.Url.Scheme, Context.Request.Url.Host, SiteUrls.Instance().UserRegister)));
                    return;
                }
                if ( Context.User.Identity.IsAuthenticated )
                {
                    CSContext.Current.User.SetExtendedAttribute( "LiveID", user.Id );
                    Users.UpdateUser( CSContext.Current.User );
                    Context.Response.Cookies.Add( new HttpCookie( "LiveID", user.Id ) );
                    State = State.In;
                    try
                    {
                        Context.Response.Redirect( HttpUtility.UrlDecode( user.Context ), true );
                    }
                    catch
                    {
                        Context.Response.Redirect( "~/" );
                    }
                }
                else
                {
                    foreach ( User csuser in Users.GetUsers().Users )
                        if ( csuser.GetExtendedAttribute( "LiveID" ) == user.Id )
                        {
                            ProcessLoginRequest( csuser, LoginUserStatus.Success, true );
                            Context.Response.Cookies.Add( new HttpCookie( "LiveID", user.Id ) );
                            try
                            {
                                Context.Response.Redirect( HttpUtility.UrlDecode( user.Context ), true );
                            }
                            catch
                            {
                                Context.Response.Redirect( "~/" );
                            }
                        }
                    Context.Response.Redirect( "~/LiveID.aspx", true );
                }
            }
            else State = State.Out;

            if ( Context.User.Identity.IsAuthenticated )
            {
                if ( Context.Request.Cookies[ "LiveID" ] != null ) State = State.In;
                else State = CSContext.Current.User.GetExtendedAttribute( "LiveID" ) == "" ? State.Associate : State.Out;
            }
            base.OnLoad( e );
        }

        #endregion

        #region Methods

        public override object DataSource
        {
            get
            {
                if ( _user == null ) _user = CSControlUtility.Instance().GetCurrentUser( this );
                return _user;
            }
            set
            {
                if ( value is User ) _user = value as User;
                else throw new InvalidCastException( "DataSource must be a valid User object" );
            }
        }

        protected override HyperLink GetHyperLink()
        {
            var user = DataSource as User;
            if ( user == null )
                return null;

            var link = new HyperLink();
            if ( State == State.Associate )
            {
                Text = ( ( Html ) LiveConfiguration.Instance.getConfigSetting( "Live ID SSO", "Associate Template" ).Value ).HtmlData;
                link.NavigateUrl = wll.GetLoginUrl( CSContext.Current.RawUrl ).Replace( "http://", "https://" );
            }
            else if ( State == State.In )
            {
                Text = ( ( Html ) LiveConfiguration.Instance.getConfigSetting( "Live ID SSO", "Signout Template" ).Value ).HtmlData;
                link.NavigateUrl = SiteUrls.Instance().Logout;
                link.Attributes.Add( "onclick",
                                     "if (confirm('Do you want to logout of your Windows Live ID as well?') == true) { window.location.href = '" +
                                     wll.GetLogoutUrl().Replace( "http://", "https://" ) + "'; return false; }" );
            }
            else
            {
                Text = ( ( Html ) LiveConfiguration.Instance.getConfigSetting( "Live ID SSO", "Signin Template" ).Value ).HtmlData;
                link.NavigateUrl = wll.GetLoginUrl( CSContext.Current.RawUrl ).Replace( "http://", "https://" );
            }
            return link;
        }

        protected void ProcessLoginRequest( User userToLogin, LoginUserStatus loginStatus, bool autoLoginUser )
        {
            CSContext csContext = CSContext.Current;

            bool enableBannedUsersToLogin = csContext.SiteSettings.EnableBannedUsersToLogin;

            if ( loginStatus == LoginUserStatus.Success ||
                 ( enableBannedUsersToLogin && loginStatus == LoginUserStatus.AccountBanned ) )
            {
                // Are we allowing login?
                if ( !csContext.SiteSettings.AllowLogin && !userToLogin.IsAdministrator )
                    throw new CSException( CSExceptionType.UserLoginDisabled );

                HttpCookie formsAuthCookie = FormsAuthentication.GetAuthCookie( userToLogin.Username, autoLoginUser );

                formsAuthCookie = AlterAuthCookie( formsAuthCookie );

                UserCookie userCookie = csContext.User.GetUserCookie();
                userCookie.WriteCookie( formsAuthCookie, 30, autoLoginUser );
            }
            else if ( loginStatus == LoginUserStatus.InvalidCredentials )
                // Invalid Credentials
                CSControlUtility.Instance().LeaveSecureConnection(
                    Globals.GetSiteUrls().Message( CSExceptionType.UserInvalidCredentials ), Context );
            else if ( loginStatus == LoginUserStatus.AccountPending )
                // Account not approved yet
                CSControlUtility.Instance().LeaveSecureConnection(
                    Globals.GetSiteUrls().Message( CSExceptionType.UserAccountPending ), Context );
            else if ( loginStatus == LoginUserStatus.AccountDisapproved )
                // Account disapproved
                CSControlUtility.Instance().LeaveSecureConnection(
                    Globals.GetSiteUrls().Message( CSExceptionType.UserAccountDisapproved ), Context );
            else if ( loginStatus == LoginUserStatus.UnknownError )
                // Unknown error because of miss-syncronization of internal data
                throw new CSException( CSExceptionType.UserUnknownLoginError );
                // Reject banned users if they are not allowed to
                // pass through login.
                //
            else if ( !enableBannedUsersToLogin && loginStatus == LoginUserStatus.AccountBanned )
                // Account banned
                CSControlUtility.Instance().LeaveSecureConnection(
                    Globals.GetSiteUrls().Message( CSExceptionType.UserAccountBanned ), Context );
        }

        protected virtual HttpCookie AlterAuthCookie( HttpCookie cookie )
        {
            return cookie;
        }

        #endregion

        #region Properties

        [Browsable( true )]
        [Bindable( false )]
        [Category( "Settings" )]
        public State State
        {
            get { return ( ViewState[ "State" ] == null ) ? State.Out : ( State ) ViewState[ "State" ]; }
            set { ViewState[ "State" ] = value; }
        }

        public Dictionary< string, Control > EditorControls
        {
            get
            {
                Dictionary< string, Control > dictionary;

                dictionary = new Dictionary< string, Control >
                                 {
                                     {
                                         "Live ID SSO Link",
                                         new Control( "Live ID SSO Link",
                                                      "Windows Live ID Single Signon Hyperlink",
                                                      true,
                                                      "<CSLive:LoginLink runat=\"server\" id=\"liveidloginlink\" />" )
                                         },
                                     {
                                         "Live ID SSO Link 2",
                                         new Control( "Live ID SSO Link 2",
                                                      "Windows Live ID Single Signon Hyperlink Without ID",
                                                      true,
                                                      "<CSLive:LoginLink runat=\"server\" />" )
                                         },
                                     {
                                         "Live ID SSO Link (Hawaii)",
                                         new Control(
                                         "Live ID SSO Link (Hawaii)",
                                         "Windows Live ID Single Signon Hyperlink",
                                         true,
                                         "<CSLive:LoginLink runat=\"server\" id=\"liveidloginlink\"><LeaderTemplate><span class=\"sub\">| </LeaderTemplate><TrailerTemplate></span></TrailerTemplate></CSLive:LoginLink>" )
                                         },
                                     {
                                         "Live ID SSO Link (Hawaii 2)",
                                         new Control(
                                         "Live ID SSO Link (Hawaii 2)",
                                         "Windows Live ID Single Signon Hyperlink Without ID",
                                         true,
                                         "<CSLive:LoginLink runat=\"server\"><LeaderTemplate><span class=\"sub\">| </LeaderTemplate><TrailerTemplate></span></TrailerTemplate></CSLive:LoginLink>" )
                                         },
                                     {
                                         "Live ID Display Condition",
                                         new Control(
                                         "Live ID Display Condition",
                                         "This control when placed uses the Live ID SSO link to determin if the signout link should be visable",
                                         false,
                                         "<CSLive:LiveLoginDisplayCondition runat=\"server\" />" )
                                         }
                                 };
                return dictionary;
            }
        }

        public string Name
        {
            get { return "Live ID SSO"; }
        }

        public Uri DocsUri
        {
            get
            {
                return
                    new Uri(
                        "http://www.codeplex.com/CSLive/Wiki/View.aspx?title=LiveID%20SSO&referringTitle=CSLive%202.0" );
            }
        }

        public string Description
        {
            get { return "Windows Live ID Single Sign On/Off"; }
        }

        public string Group
        {
            get { return "Live ID"; }
        }

        #endregion
    }
}